package cn.yang37.utils;

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @description:
 * @class: CertUtils
 * @author: yang37z@qq.com
 * @date: 2025/3/26 2:52
 * @version: 1.0
 */
@Slf4j
public class CertUtils {

    static {
        Security.addProvider(new BouncyCastleProvider());

        // 检查OID映射关系
        ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier("1.2.840.113549.1.1.11");
    }

    /**
     * 解析DER格式证书
     */
    public static X509Certificate parseDer(byte[] derData) throws CertificateException, NoSuchProviderException {
        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
        return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(derData));
    }

    /**
     * 解析PEM格式证书
     */
    public static X509Certificate parsePem(byte[] pemData) throws IOException, CertificateException {
        try (PEMParser parser = new PEMParser(new InputStreamReader(new ByteArrayInputStream(pemData)))) {
            Object obj = parser.readObject();
            if (obj instanceof X509CertificateHolder) {
                return new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder) obj);
            }
            throw new CertificateException("非X.509证书的PEM格式");
        }
    }

    /**
     * 解析证书（自动识别PEM/DER格式）
     *
     * @param certData 证书原始字节
     */
    public static X509Certificate parse(byte[] certData) throws CertificateException {
        try {
            // 尝试DER格式解析
            return parseDer(certData);
        } catch (CertificateException derEx) {
            // 尝试PEM格式解析
            try {
                return parsePem(certData);
            } catch (Exception pemEx) {
                throw new CertificateException("无法识别的证书格式", pemEx);
            }
        } catch (NoSuchProviderException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取证书详细信息
     */
    public static void getCertInfo(X509Certificate cert) {
        // 基础信息
        log.info("版本: {}", cert.getVersion());
        log.info("序列号: {}", cert.getSerialNumber().toString(16));
        log.info("生效时间: {}", cert.getNotBefore());
        log.info("失效时间: {}", cert.getNotAfter());

        // 算法信息
        String algName = cert.getSigAlgName();
        AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(algName);
        log.info("签名算法: {}", sigAlgId.getAlgorithm().getId());
        log.info("摘要算法: {}", new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId).getAlgorithm().getId());

        // 主题/颁发者信息
        log.info("主题: {}", parseX500Name(cert.getSubjectX500Principal().getName()));
        log.info("颁发者: {}", parseX500Name(cert.getIssuerX500Principal().getName()));

    }

    /**
     * 解析X500名称（兼容国密证书）
     */
    private static Map<String, String> parseX500Name(String name) {
        X500Name x500Name = new X500Name(name);
        Map<String, String> map = new LinkedHashMap<>();
        Arrays.stream(x500Name.getRDNs()).forEach(rdn -> {
            AttributeTypeAndValue attr = rdn.getFirst();
            map.put(attr.getType().getId(), // 获取OID标识符
                    attr.getValue().toString() // 获取属性值
            );
        });
        return map;
    }
}